export const CheckedMode = {
    // 只返回全选数据，包含父节点和子节点
    Full: 0,
  
    // 返回全部选中子节点和部分选中的父节点
    Half: 1,
  
    // 只返回选中子节点
    Child: 2,
  
    // 如果父节点下所有子节点全部选中，只返回父节点
    Shallow: 3
};
  
class Datum {
    constructor (options) {
        const {value, mode} = options;
        this.data = options.data;
        this.dataMap = new Map();
        this.valueMap = new Map();
        this.events = {};
        this.nodeEvents = {};
        this.mode = mode || 1;

        this.setData(this.data);
        this.setValue(value);
    }

    bind (id, update) {
        this.events[id] = update;
    }

    bindNode (id, update) {
        this.nodeEvents[id] = update;
    }
    
    unbind (id) {
        delete this.events[id];
    }

    unbindNode (id) {
        delete this.nodeEvents[id];
    }

    setValue (value) {
        this.value = value;
        if (value && value !== this.cachedValue) {
            this.initValue();
        }
    }

    getValue () {
        const value = [];
        this.valueMap.forEach((checked, id) => {
            switch (this.mode) {
                case CheckedMode.Full:
                    if (checked === 1) { value.push(id); }
                    break;
                case CheckedMode.Half:
                    if (checked >= 1) { value.push(id); }
                    break;
                case CheckedMode.Child: {
                    const children = this.links[id].children;
                    if (checked === 1 && (!children || children.length === 0)) { value.push(id); }
                    break;
                }
                case CheckedMode.Shallow:
                    if (checked === 1) {
                        const parentChecked = (() => {
                            const pid = this.links[id].parent;
                            if (!pid) { return false; }
                            return this.valueMap.get(pid) === 1;
                        })();
                        if (!parentChecked) { value.push(id); }
                    }
                    break;
                default:
            }
        });
        this.cachedValue = value;
        return value;
    }

    getOpened () {
        const ids = [];
        this.dataMap.forEach(item => {
            if (item.opened) {
                ids.push(item.id);
            }
        });
        return ids.join(',');
    }

    initValue (ids) {
        if (!this.data || !this.value) { return undefined; }
        if (!ids) {
            ids = this.levels[0];
        }
        let checked;
        ids.forEach(id => {
            const children = this.links[id].children;
            let childChecked = this.value.indexOf(id) >= 0 ? 1 : 0;
            if (children && children.length > 0) {
                childChecked = this.initValue(children);
            }
            this.setValueMap(id, childChecked);

            if (checked === undefined) { 
                checked = childChecked;
            } else if (checked !== childChecked) { 
                checked = 2; 
            }
        });
        return checked;
    }

    setValueMap (id, checked) {
        this.valueMap.set(id, checked);
        const update = this.events[id];
        if (update) { 
            update();
        }
    }

    set (id, checked, direction) {
        // self
        if (!this.isDisabled(id)) { this.setValueMap(id, checked); }
        // this.setValueMap(id, checked);
    
        const { parent, children } = this.links[id];
    
        // children
        if (direction !== 'asc') {
            children && children.forEach(cid => {
                this.set(cid, checked, 'desc');
            });
        }
    
        // parent
        if (direction !== 'desc' && parent) {
            const parentId = parent;
            let parentChecked = checked;
            this.links[parentId].children.forEach(cid => {
                if (parentChecked !== this.valueMap.get(cid)) {
                    parentChecked = 2;
                }
            });
            this.set(parentId, parentChecked, 'asc');
        }
    }

    setOpened (opened) {
        const temp = new Set(opened.split(','));
        for (const id in this.dataMap) {
            const item = this.dataMap[id];
            const hasChildren = item.children && item.children.length;
            if (hasChildren && !!item.open !== temp.has(id)) {
                item.open = !item.open;
                const update = this.nodeEvents[id];
                if (update) {
                    update('open', item.open);
                }
            }
        }
    }

    setDisabled (disabledIds) {
        const temp = new Set(disabledIds.split(','));
        for (const id in this.dataMap) {
            const item = this.dataMap[id];
            if (!!item.disabled !== temp.has(id)) {
                item.disabled = !item.disabled;
                const update = this.nodeEvents[id];
                if (update) {
                    update('disabled', item.disabled);
                }
            }
        }
    }

    setData (data) {
        this.dataMap = new Map();
        this.data = data;
        this.links = {};
        this.levels = [];
    
        if (!data) { return; }
        this.initData(null, data, 0);
        this.initValue();
    }

    get (id) {
        return this.valueMap.get(id);
    }

    // addNode (pId, node) {
    //     if (node.id) {
    //         const parent = this.dataMap[pId];
    //         if (!parent) {
    //             return;
    //         }
    //         this.dataMap[node.id] = node;
    //         parent.children = parent.children || [];
    //         parent.children.push(node);
    //         const level = parent._level + 1;
    //         if (this.levels[level]) {
    //             this.levels[level].push(node.id);
    //         } else {
    //             this.levels[level] = [node.id];
    //         }
    //         this.links[node.id] = {
    //             parent: pId
    //         };
    //         const pchildren = this.links[pId].children;
    //         if (pchildren) {
    //             pchildren.puh(node.id);
    //         } else {
    //             this.links[pId].children = [node.id];
    //         }
    //         if (node.children) {
    //             const childKeys = this.initData(node, node.children, level + 1);
    //             this.links[node.id].children = childKeys;
    //         }
            
    //         const update = this.nodeUpdates[pId];
    //         update && update();

    //         this.initValue();
    //     }
    // }

    // removeNode (id, sort) {
    //     if (id && this.dataMap[id]) {
    //         const link = this.links[id];
    //         if (!sort) {
    //             const pId = link.parent;
    //             const parent = this.dataMap[pId];
    //             const pLink = this.links[pId];
    //             if (pLink) {
    //                 const index = pLink.children.indexOf(id);
    //                 pLink.children.splice(index, 1);
    //                 let cIndex = -1;
    //                 parent.children.forEach((child, aindex) => {
    //                     if (child.id === id) {
    //                         cIndex = aindex;
    //                     }
    //                 });
    //                 if (cIndex > -1) {
    //                     console.log(11111);
                        
    //                     const children = parent.children;
    //                     children.splice(cIndex, 1);
    //                     console.log(children);
                        
    //                     parent.children = children;
    //                 }
    //             }
    //         }
    //         const children = link.children;
    //         children && children.forEach(childId => {
    //             this.removeNode(childId, 'asc');
    //         });
    //         delete this.links[id];
    //         delete this.dataMap[id];
    //         delete this.events[id];
    //         delete this.nodeEvents[id];
    //         delete this.nodeUpdates[id];

    //         if (!sort) {
    //             const pId = link.parent;
    //             const update = this.nodeUpdates[pId];
    //             update && update();
    //             console.log(update);
                
    //             this.initValue();
    //         }
    //     }
    // }
      
    getChecked (id) {
        const value = this.get(id);
        let checked = value === 1;
        if (value === 2) { checked = 'indeterminate'; }
        return checked;
    }

    isDisabled (id) {
        const node = this.dataMap.get(id);
        if (node) { return node.disabled; }
        return false;
    }

    initData (parent, data, level) {
        const ids = [];
        this.levels[level] = [];
        data.forEach((item) => {
            item._level = level;
            ids.push(item.id);
            this.dataMap[item.id] = item;
            const link = {};
            this.links[item.id] = link;
            link.parent = parent ? parent.id : null;
            this.levels[level].push(item.id);
            if (item.children) {
                const childKeys = this.initData(item, item.children, level + 1);
                link.children = childKeys;
            }
        });
        return ids;
    }
}

export default Datum;
